---
id: web
title: Web Support
sidebar_label: Web
slug: /getting-started/web
---

import {Snack} from '@site/src/components/Snack';

React Native Skia runs in the browser via [CanvasKit](https://skia.org/docs/user/modules/canvaskit/), a WebAssembly (WASM) build of Skia.
The CanvasKit WASM file, which is 2.9MB when gzipped, is loaded asynchronously.
Despite its considerable size, it offers flexibility in determining when and how Skia loads, giving you full control over the user experience.

We support direct integration with [Expo](#expo) and [Remotion](#remotion).
Additionally, you'll find manual installation steps for any webpack projects.

It should also be mentionned that React Native Skia can be used on projects without the need to install React Native Web.

## Expo

:::info

Metro and expo-router support is available from v0.1.240 and onwards.
If you are using v0.1.221 (recommended version for Expo SDK 50), you can use [this patch](https://github.com/Shopify/react-native-skia/files/14357144/%40shopify%2Breact-native-skia%2B0.1.221.patch) (using [`patch-package`](https://www.npmjs.com/package/patch-package).

:::

Use the `setup-skia-web` script to ensure that the `canvaskit.wasm` file is accessible within your Expo project's public folder.
If you're [loading CanvasKit from a CDN](#using-a-cdn), running the `setup-skia-web` script is unnecessary.

```bash
$ npx expo install @shopify/react-native-skia
$ yarn setup-skia-web
```

:::info

Run `yarn setup-skia-web` each time you upgrade the `@shopify/react-native-skia` package.
Consider incorporating it into your `postinstall` script for convenience.

:::

After setup, choose your method to [Load Skia](#loading-skia).

For projects using Expo Router, you can use [code-splitting](#using-code-splitting) or [deferred component registration](#using-deferred-component-registration).
If you wish to use deferred component registration with Expo Router, you need to create your own `main` property in `package.json`.
For instance, if you've created `index.tsx` and `index.web.tsx` in your root directory, update your `package.json` accordingly:
```patch
-  "main": "expo-router/entry",
+  "main": "index",
```

Below is an example of `index.web.tsx`:

```tsx
import '@expo/metro-runtime';
import { App } from 'expo-router/build/qualified-entry';
import { renderRootComponent } from 'expo-router/build/renderRootComponent';

import { LoadSkiaWeb } from '@shopify/react-native-skia/lib/module/web';

LoadSkiaWeb().then(async () => {
  renderRootComponent(App);
});
```

For the `index.tsx` file, directly invoke `renderRootComponent(App)`.

### Snack

Utilize the [code-splitting](#using-code-splitting) method for incorporating React Native Skia on [snack](https://snack.expo.dev/).

<Snack id="@wcandillon/hello-snack" />

## Remotion

Follow these [installation steps](https://remotion.dev/skia) to use React Native Skia with Remotion.

## Loading Skia

Ensure Skia is fully loaded and initialized before importing the Skia module.
Two methods facilitate Skia's loading:
* `<WithSkiaWeb />` for code-splitting, delaying the loading of Skia-importing components.
* `LoadSkiaWeb()` to defer root component registration until Skia loads.

### Using Code-Splitting

The `<WithSkiaWeb>` component utilizes [code splitting](https://reactjs.org/docs/code-splitting.html) to preload Skia.
The following example demonstrates preloading Skia before rendering the `MySkiaComponent`:

```tsx
import React from 'react';
import { Text } from "react-native";
import { WithSkiaWeb } from "@shopify/react-native-skia/lib/module/web";

export default function App() {
  return (
    <WithSkiaWeb
      // import() uses the default export of MySkiaComponent.tsx
      getComponent={() => import("@/components/MySkiaComponent")}
      fallback={<Text>Loading Skia...</Text>}
    />
  );
}
```
:::info

When using expo router in dev mode you cannot load components that are inside the app directory, as they will get evaluated by the router before CanvasKit is loaded.
Make sure the component to load lies outside the 'app' directory.

:::

### Using Deferred Component Registration

The `LoadSkiaWeb()` function facilitates Skia's loading prior to the React app's initiation.
Below is an `index.web.js` example:

```tsx
import { LoadSkiaWeb } from "@shopify/react-native-skia/lib/module/web";

LoadSkiaWeb().then(async () => {
  const App = (await import("./src/App")).default;
  AppRegistry.registerComponent("Example", () => App);
});
```

## Using a CDN

Below, CanvasKit loads via code-splitting from a CDN.
It is critical that the CDN-hosted CanvasKit version aligns with React Native Skia's requirements.

```tsx
import { WithSkiaWeb } from "@shopify/react-native-skia/lib/module/web";
import { version } from 'canvaskit-wasm/package.json';

export default function App() {
  return (
    <WithSkiaWeb
      opts={{ locateFile: (file) => `https://cdn.jsdelivr.net/npm/canvaskit-wasm@${version}/bin/full/${file}` }}
      getComponent={() => import("./MySkiaComponent")}
  );
}
```

Alternatively, use deferred component registration:

```tsx
import { LoadSkiaWeb } from "@shopify/react-native-skia/lib/module/web";
import { version } from 'canvaskit-wasm/package.json';

LoadSkiaWeb({
  locateFile: (file) => `https://cdn.jsdelivr.net/npm/canvaskit-wasm@${version}/bin/full/${file}`
}).then(async () => {
  const App = (await import("./src/App")).default;
  AppRegistry.registerComponent("Example", () => App);
});
```

## Unsupported Features

The following React Native Skia APIs are currently unsupported on React Native Web.
To request these features, please submit [a feature request on GitHub](https://github.com/Shopify/react-native-skia/issues/new/choose).

**Unsupported**

* `PathEffectFactory.MakeSum()`
* `PathEffectFactory.MakeCompose()`
* `PathFactory.MakeFromText()`
* `ShaderFilter`

## Manual webpack Installation

To enable React Native Skia on Web using webpack, three key actions are required:

- Ensure the `canvaskit.wasm` file is accessible to the build system.
- Configure the build system to resolve the `fs` and `path` node modules, achievable via the [node polyfill plugin](https://www.npmjs.com/package/node-polyfill-webpack-plugin).
- Update aliases for `react-native-reanimated` and `react-native/Libraries/Image/AssetRegistry` so webpack can do the bundle.

Here is an example webpack v5 configuration accommodating React Native Skia:

```tsx
import fs from "fs";
import { sources } from "webpack";
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";

const newConfiguration = {
  ...currentConfiguration,
  plugins: [
    ...currentConfiguration.plugins,
    // 1. Ensure wasm file availability
    new (class CopySkiaPlugin {
      apply(compiler) {
        compiler.hooks.thisCompilation.tap("AddSkiaPlugin", (compilation) => {
          compilation.hooks.processAssets.tapPromise(
            {
              name: "copy-skia",
              stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
            },
            async () => {
              const src = require.resolve("canvaskit-wasm/bin/full/canvaskit.wasm");
              if (!compilation.getAsset(src)) {
                compilation.emitAsset("/canvaskit.wasm", new sources.RawSource(await fs.promises.readFile(src)));
              }
            }
          );
        });
      }
    })(),
    // 2. Polyfill fs and path modules


    new NodePolyfillPlugin()
  ],
  alias: {
    ...currentConfiguration.alias,
    // 3. Suppress reanimated module warning
    // This assumes Reanimated is installed, if not you can use false.
    "react-native-reanimated/package.json": require.resolve(
      "react-native-reanimated/package.json"
    ),
    "react-native-reanimated": require.resolve("react-native-reanimated"),
    "react-native/Libraries/Image/AssetRegistry": false,
  },
}
```

Finally, proceed to [load Skia](#loading-skia).
